﻿using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace WindowsAPI.PInvoke
{
    public partial class LibraryFileInfo
    {
        public static class API
        {
            private const string kernel32 = "kernel32.dll";
            private const string user32 = "user32.dll";

            public static readonly string Kernel32_Path;

            public static readonly string User32_Path;

            static API()
            {
                Kernel32_Path = Environment.GetFolderPath(Environment.SpecialFolder.System) + "\\" + kernel32;
                User32_Path = Environment.GetFolderPath(Environment.SpecialFolder.System) + "\\" + user32;
            }

            [Flags]
            public enum LoadLibraryFlags : uint
            {
                NONE = 0,
                /// <summary>
                /// Этот флаг укапывает системе спроецировать DLL на адресное пространство вызывающего процесса. 
                /// Проецируя DLL, система обычно вызывает из нее специальную функцию DllMain и с ее помощью 
                /// инициализирует библиотеку. Флаг заставляет систему проецировать DLL, не обращаясь к DllMain.
                /// </summary>
                DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
                LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
                /// <summary>
                /// DLL проецируется на адресное пространство процесса так, будто это файл данных. 
                /// При этом система не тратит дополнительное время на подготовку к выполнению какого-либо 
                /// кода из данного файла
                /// </summary>
                LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
                LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,
                LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,
                /// <summary>
                /// Флаг изменяет алгоритм, используемый LoadLibraryEx при поиске DLL-файла.
                /// Если данный флаг установлен, функция ищет файл, просматривая каталоги в таком порядке:
                /// Каталог, заданный в napaмeтре pszDLLPathName. 
                /// 1. Текущий каталог процесса. 
                /// 2. Системный каталог Windows. 
                /// 3. Основной каталог Windows. 
                /// 4. Каталоги, перечисленные в переменной окружения PATH
                /// </summary>
                LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008
            }

            /// <summary>
            /// Загрузка библиотеки
            /// </summary>
            /// <param name="dllFilePath">Путь к файлу</param>
            /// <param name="hFile">По умолчанию IntPtr.Zero</param>
            /// <param name="dwFlags">Флаги загрузки библиотеки
            /// <para>LOAD_LIBRARY_AS_DATAFILE</para>
            /// <para>DONT_RESOLVE_DLL_REFERENCES</para>
            /// <para>LOAD_WITH_ALTERED_SEARCH_PATH</para>
            /// <para>LOAD_IGNORE_CODE_AUTHZ_LEVEL</para>
            /// </param>
            /// <returns>Дескриптор загруженного модуля</returns>
            [DllImport(kernel32)]
            public static extern IntPtr LoadLibrary(string dllFilePath);

            /// <summary>
            /// Загрузка библиотеки
            /// </summary>
            /// <param name="dllFilePath">Путь к файлу</param>
            /// <param name="hFile">По умолчанию IntPtr.Zero</param>
            /// <param name="dwFlags">Флаги загрузки библиотеки
            /// <para>LOAD_LIBRARY_AS_DATAFILE</para>
            /// <para>DONT_RESOLVE_DLL_REFERENCES</para>
            /// <para>LOAD_WITH_ALTERED_SEARCH_PATH</para>
            /// <para>LOAD_IGNORE_CODE_AUTHZ_LEVEL</para>
            /// </param>
            /// <returns>Дескриптор загруженного модуля</returns>
            [DllImport(kernel32)]
            public static extern IntPtr LoadLibraryEx(string dllFilePath, IntPtr hFile, LoadLibraryFlags dwFlags);

            public static IntPtr LoadWin32Library(string DLLFIlePath)
            {
                var lv_ModuleHandle = LoadLibrary(DLLFIlePath);
                if(lv_ModuleHandle == IntPtr.Zero)
                    throw new ApplicationException(string.Format("Не могу загрузить библиотеку {0}", DLLFIlePath),
                        new Win32Exception(Marshal.GetLastWin32Error()));

                return lv_ModuleHandle;
            }

            /// <summary>
            /// Выгрузка библиотеки из памяти 
            /// </summary>
            /// <param name="hModule">Дескриптор загруженного модуля</param>
            /// <returns>Если модуль успешно выгружен, то true, иначе - false</returns>
            [DllImport(kernel32)]
            public extern static bool FreeLibrary(IntPtr hModule);

            /// <summary>
            /// Получить дескриптор функции по её имени 
            /// </summary>
            /// <param name="dllPointer">Дескриптор модуля, в котором будет осуществляться поиск</param>
            /// <param name="functionName">Имя функции, которую требуется вызвать</param>
            /// <returns>Дескриптор функции</returns>
            [DllImport(kernel32, CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
            public extern static IntPtr GetProcAddress(IntPtr dllPointer, string functionName);

            public static Delegate GetFunction(IntPtr Handle, Type t)
            {
                return Marshal.GetDelegateForFunctionPointer(Handle, t);
            }
        }
    }
}
